﻿#region File Description
//-----------------------------------------------------------------------------
// MusicGraph.cs
//-----------------------------------------------------------------------------
#endregion

#region Using Statements

using System;
using System.Diagnostics;
using Un4seen.Bass;
using Un4seen.Bass.Misc;

#endregion


namespace kinect_music_shooter.Audio
{
    public class MusicGraph
    {
        #region Fields

        Random random = new Random();

        static int m_Sectors = 8;
        double[] m_startInSector = new double[m_Sectors];
        double m_blockTime = 1.0;


        private string _fileName = "C:/Users/Philipp/Download/Punk&Fun.mp3";

        private int _stream, _stream2;

        // create a BPMCounter instance, the timer will be fired every 20ms
        private BPMCounter _bpm = new BPMCounter(20, 44100);
        Bass Bass;
        BASSTimer timerBPM;
        BASS_CHANNELINFO info;
        String test = "_";
        private Visuals _vis = new Visuals(); // visuals class instance
        float[] m_amp = new float[8];
       /*        
        double[] m_ampMin = new double[] {
                0.5, 
                0.2, 
                0.4, 
                0.10,  
                10000,
                0.08, 
                0.04, 
                0.02
        };
        */
        double[] m_ampMin = new double[] {
                0.45, 
                0.15, 
                0.35, 
                0.05,  
                10000,
                0.075, 
                0.035, 
                0.015
        };

        private int _deviceLatencyMS = 0; // device latency in milliseconds
        private int _deviceLatencyBytes = 0; // device latency in bytes
        int delay = 0;

        #endregion


        #region Initialization

        public MusicGraph()
        {
            Bass = new Un4seen.Bass.Bass();
            
            Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
            Bass.BASS_ChannelSetAttribute(_stream, BASSAttribute.BASS_ATTRIB_TEMPO, 500);
            _stream = Bass.BASS_StreamCreateFile(_fileName, 0, 0, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_PRESCAN);
            _stream2 = Bass.BASS_StreamCreateFile(_fileName, 0, 0, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_PRESCAN);
            
            timerBPM = new BASSTimer(20);
            timerBPM.Tick += new EventHandler(timerBPM_Tick);
        }

        #endregion

        public void stop()
        {
            Bass.BASS_ChannelStop(_stream);
            Bass.BASS_ChannelStop(_stream2);           
        }

        public void pause()
        {
            Bass.BASS_ChannelPause(_stream);
            Bass.BASS_ChannelPause(_stream2);
        }

        public void resume()
        {
            // and start playing the and also start the BPM counter
            if (_stream != 0 && Bass.BASS_ChannelPlay(_stream, false))
            {
                //playing...
                _bpm.Reset(info.freq);
                // start our bpm timer callback
                this.timerBPM.Start();
            }

            Bass.BASS_ChannelPlay(_stream2, false); 
        }

        public void setSong(string file)
        {
            stop();
            _fileName = file;
            _stream = Bass.BASS_StreamCreateFile(_fileName, 0, 0, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_PRESCAN);
            _stream2 = Bass.BASS_StreamCreateFile(_fileName, 0, 0, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_PRESCAN);
        }

        public void setDelay(int delay)
        {
            this.delay = delay;
        }

        public bool getChannelIsPlaying()
        {
            //Wenn _stream2 noch läuft true sonst false
            return (Bass.BASS_ChannelIsActive(_stream2) == BASSActive.BASS_ACTIVE_PLAYING) ? true : false;
        }

        public double getTotalTime()
        {
            return Bass.BASS_ChannelBytes2Seconds(_stream2, Bass.BASS_ChannelGetLength(_stream2)); // get length in seconds
        }

        public double getCurrentTime()
        {
            return Bass.BASS_ChannelBytes2Seconds(_stream2, Bass.BASS_ChannelGetPosition(_stream2)); // get current position in seconds
        }

        public double getRemainingTime()
        {                
            return Math.Round(getTotalTime() - getCurrentTime()); // remaining
        }

        public void start()
        {
            // get the samplerate of that stream
            info = new BASS_CHANNELINFO();
            Bass.BASS_ChannelGetInfo(_stream, info);
            Bass.BASS_ChannelSetAttribute(_stream, BASSAttribute.BASS_ATTRIB_VOL, 0);

            // latency from milliseconds to bytes
            _deviceLatencyBytes = (int)Bass.BASS_ChannelSeconds2Bytes(_stream2, _deviceLatencyMS / 1000.0);

            info = new BASS_CHANNELINFO();
            Bass.BASS_ChannelGetInfo(_stream2, info);
            Bass.BASS_ChannelSetAttribute(_stream2, BASSAttribute.BASS_ATTRIB_VOL, 100);

            // latency from milliseconds to bytes
            _deviceLatencyBytes = (int)Bass.BASS_ChannelSeconds2Bytes(_stream2, _deviceLatencyMS / 1000.0);

            // and start playing the and also start the BPM counter
            if (_stream != 0 && Bass.BASS_ChannelPlay(_stream, true))
            {
                //playing...
                _bpm.Reset(info.freq);
                // start our bpm timer callback
                this.timerBPM.Start();
            }
        }

        public void play()
        {
            Stopwatch sw = new Stopwatch(); // sw cotructor
            sw.Start(); // starts the stopwatch
            for (int i = 0; ; i++)
            {
                if (i % 100000 == 0) // if in 100000th iteration (could be any other large number
                // depending on how often you want the time to be checked) 
                {
                    sw.Stop(); // stop the time measurement
                    if (sw.ElapsedMilliseconds > delay) // check if desired period of time has elapsed
                    {
                        break; // if more than 5000 milliseconds have passed, stop looping and return
                        // to the existing code
                    }
                    else
                    {
                        sw.Start(); // if less than 5000 milliseconds have elapsed, continue looping
                        // and resume time measurement
                    }
                }
            }

            // and start playing the and also start the BPM counter
            Bass.BASS_ChannelPlay(_stream2, true);
        }
	
        private void timerBPM_Tick(object sender, System.EventArgs e)
        {

            if (_stream == 0 || Bass.BASS_ChannelIsActive(_stream) != BASSActive.BASS_ACTIVE_PLAYING)
            {
                this.timerBPM.Stop();
                return;
            }
           
            bool beat = _bpm.ProcessAudio(_stream, false);

            m_amp[0] = _vis.DetectFrequency(_stream, 10, 200, true);
            m_amp[1] = _vis.DetectFrequency(_stream, 200, 500, true);
            m_amp[2] = _vis.DetectFrequency(_stream, 500, 1000, true);
            m_amp[3] = _vis.DetectFrequency(_stream, 1000, 2000, true);
            m_amp[4] = _vis.DetectFrequency(_stream, 0, 0, true);
            m_amp[5] = _vis.DetectFrequency(_stream, 2000, 5000, true);
            m_amp[6] = _vis.DetectFrequency(_stream, 5000, 10000, true);
            m_amp[7] = _vis.DetectFrequency(_stream, 10000, 20000, true);

            if (beat)
            {
                // display the live calculated BPM value
                test = _bpm.BPM.ToString("#00.0");
            }

        }

        public void setBlockTime(double blockTime)
        {
            m_blockTime = blockTime;
        }

        public double[] get_PeaksByTime()
        {          

            for (int i = 0; i < 7; i++)
            {
                if (m_amp[i] > m_ampMin[i])
                {
                    m_startInSector[i] = m_amp[i];
                }
                else
                {
                    m_startInSector[i] = 0;
                }
                 
            }          

            return m_startInSector;
        }

    }
}